package com.xyh.springboot.shiro;

import com.xyh.springboot.model.User;
import com.xyh.springboot.service.IUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @Author:kyle
 * @Date:2021/1/4 - 10:14
 * @Project:springboot
 * @Package:com.xyh.springboot.shiro
 * @Version:1.0
 */
public class UserRealm extends AuthorizingRealm {
    @Autowired
    private IUserService userService;

    /**
     * 第三步、用户授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权逻辑");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addStringPermission("user:add");

        return authorizationInfo;
    }

    /**第一步、获取数据库所有url
     * 第二步、用户认证--用户登录
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行认证逻辑");

        //1、判断用户名，token中的用户信息时登录的时候传进来的
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;

        User user = userService.findUserByUsername(token.getUsername());


        //UserRealm认证逻辑中加入密码认证加入盐
        //盐值
        ByteSource salt = ByteSource.Util.bytes(user.getSalt());

        // 验证用户是否存在，如果是数据库操作，这里肯定是userService.findByUsername(token.getUsername())操作
        if(user == null){
            return null;//返回null，shiro底层会抛出UnknownAccountException
        }

        //2、判断密码
        //参数1：需要返回给login方法的数据
        //参数2：数据库密码
        //参数3：是当前realm的名称
        //这里的对比逻辑是先对比username，当然上面已经比对了，所以肯定是相等的，
        //所以真正比对的是password，从这里传入的password(一般是数据库获取)和token(登录的时候传入的)中的password做对比
        //如果相同就允许登录，不相同，就会抛出IncorrectCredentialsException
        //如果认证的方法不通过，就不会再执行授权的方法了
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),salt,this.getName());

        //清缓存中的授权信息，保证每次登陆 都可以重新授权。因为AuthorizingRealm会先检查缓存有没有 授权信息，再调用授权方法
        super.clearCachedAuthorizationInfo(authenticationInfo.getPrincipals());

        //从Subject中获取到Session把对象保存到session中
        SecurityUtils.getSubject().getSession().setAttribute("user",user);
        return authenticationInfo;
    }
}
